<template>

  <basic-container>
    <el-page-header @back="onBack">
      <template #content>
        <div class="flex items-center">
          <el-icon>
            <Connection/>
          </el-icon>
          <el-text size="large" tag="b"> 产品草稿=></el-text>
          <el-text size="large"> {{ detail.productName }}</el-text>
        </div>
      </template>
      <template #extra>
        <div class="flex items-center">
          <el-button v-if="!this.snapshotMode" type="primary" icon="el-icon-success-filled" @click="publishOnline">
            发布上线
          </el-button>
        </div>
      </template>

      <basic-container>
        <el-descriptions :column="2" size="small">
          <el-descriptions-item label="ProductKey :">
            {{ detail.productKey }}
            <el-text type="primary" size="small" class="cursor" v-clipboard:copy="this.detail.productKey" v-clipboard:success="onCopySuccess">复制</el-text>
          </el-descriptions-item>
          <el-descriptions-item label="ProductSecret :">
            {{ detail.productSecret }}
            <el-text type="primary" size="small" class="cursor" v-clipboard:copy="this.detail.productSecret" v-clipboard:success="onCopySuccess">复制</el-text>
            <!--<el-text type="primary" size="small" class="cursor">重置</el-text>-->
          </el-descriptions-item>
        </el-descriptions>
      </basic-container>

      <el-row>
        <el-alert type="info" show-icon>
          <template #title>
            <el-text size="small">您正在编辑的是草稿，需点击</el-text>
            <el-text size="small" type="primary" class="cursor" @click="publishOnline">发布上线</el-text>
            <el-text size="small"> 后，物模型才会正式生效。</el-text>
          </template>
        </el-alert>
      </el-row>
      <el-row>
        <el-tooltip
          class="item"
          effect="dark"
          content="快速查找现有产品的物模型配置，并导入覆盖至当前产品的物模型草稿数据"
          placement="top"
        >
          <el-button @click="openImport">快速导入</el-button>
        </el-tooltip>
        <el-tooltip
          class="item"
          effect="dark"
          content="查看物模型的 TSL(Thing Specification Language) 描述语言"
          placement="top"
        >
          <el-button @click="openTsl">物模型 TSL</el-button>
        </el-tooltip>
        <el-tooltip
          class="item"
          effect="dark"
          content="将JSON格式的物模型配置文件读取，并导入覆盖至当前产品的物模型草稿数据"
          placement="top"
        >
          <el-button @click="importTsl">物模型 导入</el-button>
        </el-tooltip>
        <el-select style="width:260px" v-model="versionId"
                   filterable
                   placeholder="历史版本"
                   @change="versionChange"
                   class="margin-node">
          <el-option
              v-for="item in versionSelect"
              :key="item.id"
              :label="`${item.versionName}`"
              :value="item.id">
            <span style="float: left">{{ item.versionName }}</span>
            <span style="float: right;color: #8492a6;font-size: 13px;">
              {{ item.versionTime }}
            </span>
          </el-option>
        </el-select>
        <el-button v-if="this.versionId > 0" class="margin-node" @click="versionRecovery">恢复此版本</el-button>
        <el-button v-if="this.versionId > 0" class="margin-node" @click="versionReset">返回原版本</el-button>
      </el-row>
      <el-row>
        <el-col :span="3">
          <avue-tree ref="tree"
                     :option="optionTree"
                     :data="dataTree"
                     v-model="formTree"
                     :filter-node-method="filterNodeMethod"
                     @update="updateTree"
                     @save="saveTree"
                     @node-click="nodeClickTree">
            <template #default="{ node, data }">
              <span class="custom-tree-node">
                <span v-if="data.isDefault === 1">默认模块</span>
                <span v-else>{{ node.label }}</span>
                <span v-if="this.showNode(data)" class="operation-icons">
                    <el-icon @click.stop="updateNode(data)" class="icon-btn"><Edit/></el-icon>
                    <el-icon @click.stop="delNode(data)" class="icon-btn"><Delete/></el-icon>
                </span>
              </span>
            </template>
          </avue-tree>
          <el-dialog v-model="visible" :before-close="beforeCloseBlock" title="编辑模块" width="450">
            <avue-form ref="formBlock" v-model="formBlock" :option="optionTree.formOption"
                       @submit="handleUpdate"></avue-form>
          </el-dialog>
        </el-col>
        <el-col :span="1">
          <el-dialog v-model="visibleImport" :before-close="beforeCloseImport" title="物模型快速导入" width="500">
            <el-row>
              <el-alert title="导入物模型将覆盖现有草稿数据" type="warning" show-icon :closable="false"/>
            </el-row>
            <el-row>
              <avue-form ref="formImport"
                         :option="optionImport"
                         v-model="formImport"
                         @submit="handleImport">
              </avue-form>
            </el-row>
          </el-dialog>
          <el-dialog v-model="visibleTsl" :before-close="beforeCloseTsl" title="物模型" width="600">
            <el-row>
              <el-alert
                  title="物模型是云端对设备功能的抽象描述，涵盖了设备的属性、服务和事件。物联网平台通过物的描述语言，即 TSL（Thing Specification Language），以 JSON 格式表达这一模型。开发者可以利用 TSL 构建并上报设备数据。完整的物模型可用于云端应用的开发，而精简版的物模型则可结合设备端SDK用于设备的开发工作。"
                  type="info" :closable="false"/>
            </el-row>
            <el-row>
              <el-select
                  v-model="blockId"
                  clearable
                  placeholder="请选择模块"
                  @change="blockTslChange"
                  style="width: 240px"
              >
                <el-option
                    v-for="item in dataTree"
                    :key="item.value"
                    :label="item.isDefault === 1 ? '默认模块': item.title"
                    :value="item.value"
                />
              </el-select>
            </el-row>
            <el-row>
              <code-editor v-model="tsl" :json="true" :readonly="true" theme="nord"/>
            </el-row>
          </el-dialog>
          <el-dialog v-model="visibleTslImport" :before-close="beforeCloseTslImport" title="物模型导入" width="600">
            <el-upload
              drag
              accept=".json"
              :show-file-list="false"
              :action="'/api/blade-iot/product/version/tsl-import?productId=' + this.productId"
              :headers="uploadHeaders"
              :on-success="uploadSuccess"
              :on-error="uploadError"
            >
              <el-icon class="el-icon--upload"><upload-filled /></el-icon>
              <div class="el-upload__text">
                拖动文件上传 <em>或者点击上传</em>
              </div>
              <template #tip>
                <div class="el-upload__tip">
                  请上传JSON格式的物模型文件
                </div>
              </template>
            </el-upload>
          </el-dialog>
        </el-col>
        <el-col :span="20">
          <avue-crud ref="crud"
                     :option="option"
                     :data="data"
                     v-model="form"
                     :table-loading="loading"
                     :before-open="beforeOpen"
                     :before-close="beforeClose"
                     @row-save="rowSave"
                     @row-update="rowUpdate"
                     @row-del="rowDel"
                     @on-load="onLoad">
            <template #menu-left>
              <el-button v-if="!this.snapshotMode" type="primary"
                         icon="el-icon-circle-plus"
                         @click="$refs.crud.rowAdd()">添加功能
              </el-button>
            </template>
            <template #functionType="{ row }">
              <el-tag style="cursor: pointer">{{ row.functionTypeName }}</el-tag>
            </template>
            <template #specEnum-form="{}">
              <function-enum v-model="enumsData" :snapshot-mode="snapshotMode"></function-enum>
            </template>
            <template #specStruct-form="{}">
              <function-struct v-model="structData" :snapshot-mode="snapshotMode"></function-struct>
            </template>
            <template #specInput-form="{}">
              <function-struct-full v-model="inputData" :snapshot-mode="snapshotMode"></function-struct-full>
            </template>
            <template #specOutput-form="{}">
              <function-struct-full v-model="outputData" :snapshot-mode="snapshotMode"></function-struct-full>
            </template>
          </avue-crud>
        </el-col>
      </el-row>
    </el-page-header>
  </basic-container>
</template>

<script>
import {getDetailByKey} from "@/api/iot/product";
import {
  select as selectVersion,
  dataSnapshot,
  dataImport,
  dataRecovery,
  dataDraftedTsl,
} from "@/api/iot/product/version";

import {mapGetters} from "vuex";
import {dataTypeName, fieldTypeName, option, optionProductImport, optionTree} from '@/option/iot/function'
import {
  getList as functionList,
  getDetail as functionDetail,
  add as functionAdd,
  update as functionUpdate,
  remove as functionRemove,
  publishOnline
} from "@/api/iot/product/function";
import {
  getBlockList,
  getDetail as blockDetail,
  add as addBlock,
  update as updateBlock,
  remove as removeBlock
} from "@/api/iot/product/block";
import {validatenull} from "@/utils/validate";
import func from "@/utils/func";
import { getToken } from '@/utils/auth';

export default {
  name: "productDraft",
  data() {
    return {
      activeName: 'info',
      blockId: '',
      productId: '',
      productKey: '',
      form: {},
      formImport: {},
      tsl: {},
      detail: {},
      query: {},
      search: {},
      loading: false,
      visible: false,
      visibleImport: false,
      visibleTsl: false,
      visibleTslImport: false,
      versionId: "",
      versionSelect: [],
      snapshotMode: false,
      option: option(this),
      optionImport: optionProductImport,
      optionTree: optionTree,
      formBlock: {},
      formTree: {},
      formVersion: "",
      data: [],
      dataTree: [],
      structData: [],
      enumsData: [],
      inputData: [],
      outputData: [],
      dataSnapshot: {},
      functionSnapshot: [],
      blockSnapshot: [],
      uploadHeaders: {
        'Blade-Auth': `bearer ${getToken()}`,
        'Blade-Requested-With': 'BladeHttpRequest'
      },
    };
  },
  computed: {
    ...mapGetters(["permission"]),
  },
  watch: {
    $route(to, from) {
      if (to.path !== from.path) {
        this.init();
      }
    },
    snapshotMode() {
      if (this.snapshotMode) {
        this.snapshotOperation();
      } else {
        this.editOperation();
      }
    },
  },
  mounted() {
    this.editOperation();
  },
  created() {
    this.init();
  },
  methods: {
    init() {
      this.productId = this.$route.params.productId;
      this.productKey = this.$route.params.productKey;
      this.versionId = "";
      getDetailByKey(this.productKey).then(res => {
        this.detail = res.data.data;
      });
      this.initBlock(this.productId);
    },
    initBlock(productId) {
      selectVersion(productId).then(res => {
        this.versionSelect = res.data.data;
      });
      getBlockList(productId).then(res => {
        this.dataTree = res.data.data;
        let defaultItem = this.dataTree.find(item => item.isDefault === 1);
        if (!validatenull(defaultItem)) {
          this.$refs.tree.setCurrentKey(defaultItem.key);
          this.blockId = defaultItem.key;
          this.blockTslChange(this.blockId);
          this.onLoad();
        }
      });
    },
    onBack() {
      this.$router.push({
        path: `/iot/product/detail/${this.productKey}/${this.productId}`,
        query: {
          activeKey: 'function'
        }
      });
      this.$router.$avueRouter.closeTag();
    },
    snapshotOperation() {
      this.$refs.tree.option.addBtn = false;
      this.$refs.crud.option.editBtn = false;
      this.$refs.crud.option.delBtn = false;
      this.$refs.crud.option.viewBtn = true;
    },
    editOperation() {
      this.$refs.tree.option.addBtn = true;
      this.$refs.crud.option.editBtn = true;
      this.$refs.crud.option.delBtn = true;
      this.$refs.crud.option.viewBtn = false;
    },
    openImport() {
      this.visibleImport = true;
    },
    beforeCloseImport() {
      this.$refs.formImport.resetForm();
      this.visibleImport = false;
    },
    handleImport(form, done, loading) {
      this.$confirm('即将覆盖现有数据无法找回，是否导入此版本?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        dataImport(form.versionId, this.productId).then(() => {
          this.$message({
            type: "success",
            message: "导入成功!"
          });
          this.beforeCloseImport();
          this.init();
          done();
        }, error => {
          loading();
          window.console.log(error);
        });
      }).catch(() => {
        done();
      });
    },
    uploadSuccess(response) {
      console.log(response);
      this.$confirm('物模型数据导入完毕，是否刷新数据?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.init();
        this.beforeCloseTslImport();
      })
    },
    uploadError(error) {
      console.log(error.message);
      this.$message.error("导入失败请检查文件格式！");
    },
    versionChange(value) {
      this.versionId = value;
      dataSnapshot(value).then(res => {
        this.dataSnapshot = res.data.data;
        // 将三个列表合并返回，并且根据默认的blockId筛选出对应的列表
        // 树形点击的时候判断versionId是否为空，如果为空则调用接口，否则就循环snapshot的值
        // 选中版本后，给versionId设置值。恢复版本或者返回版本后给versionId设置空
        const properties = this.dataSnapshot.properties;
        const commands = this.dataSnapshot.commands;
        const events = this.dataSnapshot.events;
        // 设置功能列表快照
        this.functionSnapshot = [...properties, ...commands, ...events];
        // 循环snapshot的值，将blockName改成title，id改成value然后返回
        const blockSnapshot = this.dataSnapshot.blocks.map(item => {
          return {
            id: item.id,
            key: item.id,
            value: item.id,
            title: item.blockName,
            tag: item.tag,
            isDefault: item.isDefault
          }
        });
        this.blockSnapshot = blockSnapshot;
        this.dataTree = blockSnapshot;
        // 选中默认的模块
        let defaultItem = this.dataTree.find(item => item.isDefault === 1);
        if (!validatenull(defaultItem)) {
          this.$refs.tree.setCurrentKey(defaultItem.key);
          this.blockId = defaultItem.key;
          this.data = this.functionSnapshot.filter(item => item.blockId === defaultItem.key);
        }
        this.snapshotMode = true;
      });
    },
    versionRecovery() {
      this.$confirm('即将覆盖现有数据无法找回，是否恢复此版本?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        dataRecovery(this.versionId).then(() => {
          this.$message({
            type: "success",
            message: "操作成功!"
          });
          this.versionReset();
        });
      })
    },
    versionReset() {
      this.versionId = "";
      this.dataSnapshot = {};
      this.blockSnapshot = [];
      this.functionSnapshot = [];
      this.snapshotMode = false;
      this.init();
    },
    beforeCloseBlock() {
      this.$refs.formBlock.resetForm();
      this.visible = false;
    },
    nodeClickTree(data) {
      this.blockId = data.key;
      if (this.snapshotMode) {
        // 快照模式下从缓存的列表中筛选数据
        this.data = this.functionSnapshot.filter(item => item.blockId === data.key);
      } else {
        // 数据库模式下从接口中获取数据
        this.onLoad();
      }
    },
    filterNodeMethod(value, data) {
      if (!value) return true;
      return data.label.indexOf(value) !== -1;
    },
    saveTree(node, data, done, loading) {
      if (func.isEmpty(data.parentId)) {
        data.parentId = 0;
      }
      data.productId = this.productId;
      addBlock(data).then(() => {
        this.$message({
          type: "success",
          message: "操作成功!"
        });
        this.initBlock(this.productId);
        done();
      }, error => {
        loading();
        window.console.log(error);
      });
    },
    updateTree(node, data, done, loading) {
      updateBlock(data).then(() => {
        this.$message({
          type: "success",
          message: "操作成功!"
        });
        this.initBlock(this.productId);
        done();
      }, error => {
        loading();
        window.console.log(error);
      });
    },
    handleUpdate(form, done, loading) {
      updateBlock(form).then(() => {
        this.$message({
          type: "success",
          message: "操作成功!"
        });
        this.visible = false;
        this.$refs.formBlock.resetForm();
        this.initBlock(this.productId);
        done();
      }, error => {
        loading();
        window.console.log(error);
      });
    },
    showNode(data) {
      return data.isDefault !== 1 && !this.snapshotMode;
    },
    updateNode(data) {
      blockDetail(data.value).then(res => {
        this.formBlock = res.data.data;
        this.visible = true;
      });
    },
    delNode(data) {
      this.$confirm(`是否删除[${data.title}]?`, '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        removeBlock(data.value).then(() => {
          this.initBlock(this.productId);
          this.$message({
            type: "success",
            message: "操作成功!"
          });
        }, error => {
          window.console.log(error);
        });
      })
    },
    transForm(row) {
      // 功能类型
      const functionType = this.form?.functionType;
      if (functionType === 2) {
        // 输入参数
        this.inputData = row.specInput;
        // 输出参数
        this.outputData = row.specOutput;
      }
      if (functionType === 3) {
        // 输出参数
        this.outputData = row.specOutput;
      }
      // 属性功能
      if (functionType === 1) {
        if (row.specFunction.fieldType === "enum") {
          // 枚举数据
          this.enumsData = func.keyValueToJsonArray(row.specFunction.specEnum);
        }
        if (row.specFunction.fieldType === "struct") {
          // 属性结构体
          this.structData = row.specFunction.specStruct;
        }
        row.specFunctionId = row.specFunction.id; // 功能主键
        row.fieldType = row.specFunction.fieldType; // 数据类型
        row.specMin = row.specFunction.specMin; // 最小值
        row.specMax = row.specFunction.specMax; // 最大值
        row.specStep = row.specFunction.specStep; // 步长
        row.specUnit = row.specFunction.specUnit; // 单位
        row.specEnum = row.specFunction.specEnum; // 枚举数据
        row.specBoolFalse = row.specFunction.specBoolFalse; // 布尔值 0
        row.specBoolTrue = row.specFunction.specBoolTrue; // 布尔值 1
        row.specLength = row.specFunction.specLength; // 数据长度
        row.specDateFormat = row.specFunction.specDateFormat; // 时间格式
        row.specItemType = row.specFunction.specItemType; // 元素类型
        row.specSize = row.specFunction.specSize; // 元素个数

      }
      return row;
    },
    buildForm(row) {
      // 功能类型
      const functionType = this.form?.functionType;
      // 模块id
      row.blockId = this.blockId;
      // 产品id
      row.productId = this.productId;
      // 结构体参数默认为空
      row.specFunction = {};
      // 输入参数默认为空
      row.specInput = [];
      // 输出参数默认为空
      row.specOutput = [];
      if (functionType === 2) {
        // 输入参数
        row.specInput = this.inputData;
        // 输出参数
        row.specOutput = this.outputData;
      }
      if (functionType === 3) {
        // 输出参数
        row.specOutput = this.outputData;
      }
      // 属性功能
      if (functionType === 1) {
        if (row.fieldType === "enum") {
          // 枚举参数
          row.specEnum = func.jsonArrayToKeyValue(this.enumsData);
        }
        row.specFunction = {
          id: row.specFunctionId, // 功能主键
          functionType: row.functionType, // 功能类型
          name: row.name, // 功能名称
          identifier: row.identifier, // 标识符
          fieldType: row.fieldType, // 数据类型
          specMin: row.specMin, // 最小值
          specMax: row.specMax, // 最大值
          specStep: row.specStep, // 步长
          specUnit: row.specUnit, // 单位
          specEnum: row.specEnum, // 枚举数据
          specBoolFalse: row.specBoolFalse, // 布尔值 0
          specBoolTrue: row.specBoolTrue, // 布尔值 1
          specLength: row.specLength, // 数据长度
          specDateFormat: row.specDateFormat, // 时间格式
          specItemType: row.specItemType, // 元素类型
          specSize: row.specSize, // 元素个数
          specStruct: (
              row.fieldType === "struct" || row.specItemType === "struct"
          ) ? this.structData : [] // 结构体参数
        };
      }
      return row;
    },
    openTsl() {
      if (!validatenull(this.productId) && !validatenull(this.blockId)) {
        dataDraftedTsl(this.productId, this.blockId).then(res => {
          this.tsl = JSON.stringify(res.data.data);
        });
      }
      this.visibleTsl = true;
    },
    importTsl() {
      this.$confirm('即将覆盖现有数据无法找回，是否继续？', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          this.visibleTslImport = true;
        })
    },
    beforeCloseTsl() {
      this.tsl = "";
      this.visibleTsl = false;
    },
    beforeCloseTslImport() {
      this.visibleTslImport = false;
    },
    blockTslChange(value) {
      this.blockId = value;
      dataDraftedTsl(this.productId, this.blockId).then(res => {
        this.tsl = JSON.stringify(res.data.data);
      });
    },
    beforeOpen(done, type) {
      if (["edit"].includes(type)) {
        functionDetail(this.form.id, this.form.functionType).then(res => {
          const data = res.data.data;
          this.form = this.transForm(data);
          if (data.functionType === 1) {
            this.form.fieldType = data.specFunction.fieldType;
            this.form.specFunctionId = data.specFunction.id;
          }
        });
      }
      if (["view"].includes(type)) {
        const data = this.functionSnapshot.find(item => item.id === this.form.id);
        this.form = this.transForm(data);
        if (data.functionType === 1) {
          this.form.fieldType = data.specFunction.fieldType;
          this.form.specFunctionId = data.specFunction.id;
        }
      }
      done();
    },
    beforeClose(done) {
      this.form = {};
      this.structData = [];
      this.enumsData = [];
      this.inputData = [];
      this.outputData = [];
      done();
    },
    rowSave(row, done, loading) {
      functionAdd(this.buildForm(row)).then(() => {
        this.onLoad();
        this.$message({
          type: "success",
          message: "操作成功!"
        });
        done();
      }, error => {
        loading();
        window.console.log(error);
      });
    },
    rowUpdate(row, index, done, loading) {
      functionUpdate(this.buildForm(row)).then(() => {
        this.onLoad();
        this.$message({
          type: "success",
          message: "操作成功!"
        });
        done();
      }, error => {
        loading();
        console.log(error);
      });
    },
    rowDel(row) {
      this.$confirm("确定将选择数据删除?", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      }).then(() => {
        return functionRemove(row.id, row.functionType);
      }).then(() => {
        this.onLoad();
        this.$message({
          type: "success",
          message: "操作成功!"
        });
      });
    },
    onLoad(params = {}) {
      const productId = this.productId;
      const blockId = this.blockId;
      if (validatenull(productId) || validatenull(blockId)) {
        return;
      }
      params = {
        ...params,
        productId,
        blockId
      }
      this.loading = true;
      functionList(params).then(res => {
        this.data = res.data.data.map(item => {
          return {
            ...item,
            fieldTypeName: fieldTypeName(item),
            dataTypeName: dataTypeName(item)
          }
        });
        this.loading = false;
      });
    },
    publishOnline() {
      this.$prompt('发布备注', '发布物模型上线', {
        confirmButtonText: '发布',
        cancelButtonText: '关闭',
      }).then(({value}) => {
        publishOnline(this.productId, value).then(() => {
          this.$message({
            type: "success",
            message: "发布成功!"
          });
          this.onBack();
        });
      })
    },
    onCopySuccess() {
      this.$message.success('复制成功');
    },
  }
};
</script>

<style>
.cursor {
  cursor: pointer;
}

.el-tree-node__content {
  display: flex;
  width: 100%;
  align-items: center;
}

.custom-tree-node {
  flex-grow: 1;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.operation-icons {
  display: flex;
  visibility: hidden;
}

.el-tree-node__content:hover .operation-icons {
  visibility: visible;
}

.icon-btn {
  cursor: pointer;
  margin-left: 8px;
}

.margin-node {
  margin-left: 12px;
}
</style>
